// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;

namespace SnapObjects.Data
{
    internal class UpdateTracker<TModel>
        : ModelTracker<TModel>, IEmbeddedStateTracker
    {
        public UpdateTracker(
            IMapperContext mapperContext,
            IStatefulModel statefulModel,
            Action<ISaveContext> beforeSaveAction,
            Action<ISaveContext> afterSaveAction,
            bool notCheckedOut)
            : base(mapperContext, (TModel)statefulModel.Current,
                    beforeSaveAction, afterSaveAction)
        {
            _statefulModel = statefulModel;

            if (statefulModel.ModelState == ModelState.NotModified &&
                !notCheckedOut)
            {
                _statefulModel.CheckedOut = true;
            }

            if (statefulModel.ModelState != ModelState.NotModified &&
                statefulModel.ModelState != ModelState.Modified)
            {
                throw new InvalidOperationException("ModelState needs to be NotModified or Modified, current ModelState: " + statefulModel.ModelState);
            }
        }

        public UpdateTracker(
            IMapperContext mapperContext,
            TModel originalModel,
            TModel currentModel,
            Action<ISaveContext> beforeSaveAction,
            Action<ISaveContext> afterSaveAction,
            bool notCheckedOut)
            : this(mapperContext, CreateStatefulModel(mapperContext, originalModel, currentModel), beforeSaveAction, afterSaveAction, notCheckedOut)
        {
        }

        public UpdateTracker(
            IMapperContext mapperContext,
            TModel model,
            Action<ISaveContext> beforeSaveAction,
            Action<ISaveContext> afterSaveAction)
            : base(mapperContext, model, beforeSaveAction, afterSaveAction)
        {
            var statefulModel = this.CreateStatefulModel(model, ModelState.NotModified);
            statefulModel.CheckedOut = true;
        }

        private static IStatefulModel CreateStatefulModel(
            IMapperContext mapperContext,
            TModel originalModel,
            TModel currentModel,
            ChangeTrackingStrategy changeTrackingStrategy = ChangeTrackingStrategy.Snapshot)
        {
            var compositeModelAccessor = mapperContext.GetCompositeModelAccessor(typeof(TModel));

            var statefulModel = PocoStatefulModelFactory.Create(compositeModelAccessor as ICompositeModelAccessor<TModel>,
                                originalModel, currentModel, changeTrackingStrategy);

            statefulModel.CheckedOut = true;

            return statefulModel;
        }

        protected override IStatefulModel StatefulModel => _statefulModel;

        protected override void BuildUpdateableModels(object model)
        {
            var modelMeta = ModelMetaManager.GetModelMeta(model.GetType());

            _updateBuilder.Append(
                        new UpdatableModel<TModel>(
                                modelMeta,
                                ChangeTrackingStrategy.Snapshot,
                                modelMeta.UpdateWhereStrategy,
                                _statefulModel,
                                true,
                                _beforeSaveAction,
                                _afterSaveAction,
                                this));
        }

        public ModelState GetEmbeddedState(object model)
        {
            var entry = _mapperContext.GetModelEntry(model);

            return entry == null ? ModelState.NotTracked : entry.ModelState;
        }
    }
}
